#ifndef UTILITY_C
#define UTILITY_C
#include "utility.h"
#include <cstdio>
#include <cmath>

template<typename T> void print_device_array(const T* device_array, int size)
{
	T * host_array = new T[size];
	cudaMemcpy(host_array, device_array, sizeof(T)*size, cudaMemcpyDeviceToHost);
	for(int i=0; i<size; i++)
		printf("%f ",(float)host_array[i]);
	printf("\n");
	delete [] host_array;
}

void print_vector(const CUDA_vec<float> & a){
	float * array = new float[a.getSize()];
	a.extractFromDevice(array);
	printf("[");
	for(int i=0; i<a.getSize(); i++)
	{
		printf("%f",array[i]);
		if(i!=a.getSize()-1)
			printf(",");
	}
	printf("]");
	delete [] array;
}

void print_2Dvector(const CUDA_vec<float> &a)
{
	float * array = new float[a.getSize()];
	a.extractFromDevice(array);
	printf("[");
	int N = (int)sqrt(a.getSize());
	for(int i=0; i<N; i++)
	{
		printf("[");
		for(int j=0; j<N; j++)
		{
			printf("%f",array[i* N + j]);
			if(j!=N-1)
				printf(", ");
		}
		printf("]");
		if(i!=N-1)
			printf(",\n");

	}
	printf("]\n");
	delete [] array;

}
void print_CUDA_err_msg(const char * msg)
{
	cudaError_t err = cudaGetLastError();
	if( cudaSuccess != err)
	{
		fprintf(stderr, "Cuda error: %s; %s.\n", msg,
				cudaGetErrorString( err) );

		exit(EXIT_FAILURE);
	}

}

dim3 max_dim_block(int devnum)
{
	int  ndev;
	cudaGetDeviceCount(&ndev);
	cudaThreadSynchronize();
	if(devnum >= ndev)
		devnum = ndev-1;
	cudaDeviceProp pdev;
	cudaGetDeviceProperties(&pdev,devnum);
	cudaThreadSynchronize();
	dim3 maxdimBlock(pdev.maxThreadsDim[0], pdev.maxThreadsDim[1], pdev.maxThreadsDim[2]);
	return maxdimBlock;
}

dim3 max_dim_grid(int devnum)
{
	int  ndev;
	cudaGetDeviceCount(&ndev);
	cudaThreadSynchronize();
	if(devnum >= ndev)
		devnum = ndev-1;
	cudaDeviceProp pdev;
	cudaGetDeviceProperties(&pdev,devnum);
	cudaThreadSynchronize();
	dim3 maxdimGrid(pdev.maxGridSize[0], pdev.maxGridSize[1], pdev.maxGridSize[2]);
	return maxdimGrid;
}

int max_threads_per_block(int devnum){
	int  ndev;
	cudaGetDeviceCount(&ndev);
	cudaThreadSynchronize();
	if(devnum >= ndev)
		devnum = ndev-1;
	cudaDeviceProp pdev;
	cudaGetDeviceProperties(&pdev,devnum);
	cudaThreadSynchronize();
	return pdev.maxThreadsPerBlock;
}
dim3 optimal_block_size(int devnum, int dimension)
{
	dim3 maxblockDim = max_dim_block(devnum);
	int maxThreads = max_threads_per_block(devnum);
	int maxThreadsperDim = (int)(floor(pow(float(maxThreads), 1/float(dimension))));
	dim3 optimalDim(min(maxblockDim.x, maxThreadsperDim), min(maxblockDim.y, maxThreadsperDim), min(maxblockDim.z, maxThreadsperDim));
	return optimalDim;
}
void print_dim(dim3 d)
{
#ifdef DEBUG
	printf("x: %d y: %d z: %d\n", d.x, d.y, d.z);
#endif
}

#endif
